<template>
  <!--components/camera-face/index.wxml-->
  <!--人脸识别录像组件-->
  <view class="page-body">
    <view class="camera-box">
      <camera mode="normal" :device-position="devicePosition" :frame-size="frameSize" :resolution="resolution"
        :flash="flash" binderror="error" bindstop="stop" class="camera">
      </camera>
      <view class="img-view">
        <image mode="scaleToFill" class="mask-img" src="static/images/mask.png"></image>
      </view>
    </view>
    <view class="bottom-view">
      <view v-if="!bottomTips" class="bottom-btn" @click="readyRecord">准备录制人脸</view>
      <view v-else class="bottom-tips">{{bottomTips}}</view>
    </view>
  </view>

</template>

<script>
  import {
    getAuthorize,
    setAuthorize,
    throttle,
    checkVersion,
  } from '@/common/faceUtil.js'

  export default {
    data() {
      return {
        personImagesUploadFlag: false, // 人脸上传防抖 不然一下子会上传三次
        tips: { // 提示信息
          ready: '请确保光线充足,正面镜头',
          recording: '人脸录制中..',
          complete: '已录制完成',
          error: '录制失败',
          away: '请离远一点',
          pitch: ' 请上下点头',
          roll: '请左右摇头'
        },
        // 人脸整体可信度 [0-1], 参考wx.faceDetect文档的res.confArray.global
        // 当超过这个可信度且正脸时开始录制人脸, 反之停止录制
        faceCredibility: 0.9,
        // 人脸偏移角度正脸数值参考wx.faceDetect文档的res.angleArray
        // 越接近0越正脸，包括p仰俯角(pitch点头）, y偏航角（yaw摇头), r翻滚角（roll左右倾）
        faceAngle: {
          p: 0.5,
          y: 0.5,
          r: 0.5
        },
        // 录制视频时长,不能超过30s
        duration: 3000,
        // 是否压缩视频
        compressed: false,
        // 前置或者后置 front,back
        devicePosition: 'front',
        // 指定期望的相机帧数据尺寸 small,medium,large
        frameSize: 'medium',
        // 分辨率 low,medium,high
        resolution: 'medium',
        // 闪光灯 auto,on,off,torch
        flash: 'off',
        // 检测视频帧的节流时间，默认500毫秒执行一次
        throttleFrequency: 500,
        isReading: false, // 是否在准备中
        isRecoding: false, // 是否正在录制中
        isStopRecoding: false, // 是否正在停止录制中
        bottomTips: '', // 底部提示文字
        pitch_old: 0,
        yaw_old: 0,
        roll_old: 0,
        pitch_Threshold: 0.2, //这里调整点头阈值
        roll_Threshold: 0.2, //这是摇头阈值
        face_away: 0.8,
        pitch_flag: false,
        yaw_flag: false,
        pict_flag: false
      };
    },
    onHide() {
      this.stop()
    },
    beforeDestroy() {
      this.stop()
    },
    methods: {
      // 上传人脸图片
      setImageContractSan(img){
        if(this.personImagesUploadFlag){
          console.log('防抖生效');
          return
        }
        this.personImagesUploadFlag = true
        let token = uni.getStorageSync('token')
        // 接下来就是上传文件
        console.log('接下来就是上传文件',img);
          uni.uploadFile({
            url: this.path + '/person/registerFace',
            filePath: img,
            name: 'file',
            header: {
              Authorization: token
            },
            formData: {
              Authorization: token,
              filePath: img, 
              auditGuid: uni.getStorageSync('admitGuid'), //上传人员唯一值 和 人员照片
              subpack: uni.getStorageSync('nowSubpackName'), // 分包单位名称  注册哪一个分包单位 
              unitId: uni.getStorageSync('nowrelativeUnitId'),
              projectId: uni.getStorageSync('nowrelativeProjectId')
            },
            timeout: 10000,
            // 如果上传成功
            success: (res) => {
              console.log('人脸上传成功 返回值为: ',res);
              // this.personImagesUploadFlag = false
              uni.hideLoading()
              let perstatus = JSON.parse(res.data);
              console.log('人脸上传成功 返回值为: ',perstatus);
              // 文件上传成功之后 判断上传的文件是否判断成功
              //判断函数回调是否验证成功
			  if(JSON.parse(res.data).code==1000){
				  uni.hideLoading()
				  uni.showToast({
				    title: '人脸注册成功 !',
				    icon: 'success',
				    duration: 700
				  })
				  //验证成功 跳转成功页面
				  setTimeout(() => {
				    this.navTo(1)
				  }, 700)
				  return 
			  }
              if (perstatus.success == true) {
                //人脸验证成功
                uni.hideLoading()
                uni.showToast({
                  title: '人脸注册成功 !',
                  icon: 'success',
                  duration: 700
                })
                //验证成功 跳转成功页面
                setTimeout(() => {
                  this.navTo(1)
                }, 700)
                return true
              } else {
                // 人脸验证失败
                console.log('人脸验证失败');
                uni.showToast({
                  title: perstatus.message
                })
                uni.hideLoading()
                setTimeout(() => {
                  uni.switchTab({
                    url: '/pages/example/home/index'
                  })
                }, 1000)
                return false
              }
            },
            //如果上传失败
            //提示上传失败 然后停在原地 让用户重新拍照
            fail: (res) => {
              // this.personImagesUploadFlag = false
              uni.showToast({
                title: '人脸上传失败 请稍候重试',
                icon: 'error'
              })
              uni.hideLoading()
            }
          })
      },
      
      // 开启相机ctx
      async start() {
        const result = await this.initAuthorize();
        if (!result) return false;
        if (!this.ctx) this.ctx = wx.createCameraContext();
        return true;
      },

      // 准备录制
      async readyRecord() {
        if (this.isReading) return
        this.isReading = true
        wx.showLoading({
          title: '加载中..',
          mask: true
        })
        // 检测版本号
        const canUse = checkVersion('2.18.0', () => {
          // this.triggerEvent('cannotUse')
        })
        if (!canUse) {
          wx.hideLoading()
          this.isReading = false
          return
        }

        // 启用相机
        try {
          const result = await this.start()
          if (!result || !this.ctx) throw new Error()
        } catch (e) {
          wx.hideLoading()
          this.isReading = false
          return
        }
        console.log('准备录制')
        this.bottomTips = this.tips.ready
        // 视频帧回调节流函数
        let fn = throttle((frame) => {
          // 人脸识别
          wx.faceDetect({
            frameBuffer: frame.data,
            width: frame.width,
            height: frame.height,
            enableConf: true,
            enableAngle: true,
            success: (res) => this.processFaceData(res),
            fail: (err) => this.cancel()
          })
        }, this.throttleFrequency);

        // 初始化人脸识别
        wx.initFaceDetect({
          success: () => {
            const listener = this.listener = this.ctx.onCameraFrame((frame) => fn(frame));
            listener.start();
          },
          fail: (err) => {
            console.log('初始人脸识别失败', err)
            this.bottomTips = ''
            wx.showToast({
              title: '初始人脸识别失败',
              icon: 'none'
            })
          },
          complete: () => {
            wx.hideLoading()
            this.isReading = false
          }
        })
      },

      // 处理人脸识别数据
      processFaceData(res) {
        // console.log('人脸识别接口的返回值', res);
        if (res.confArray && res.angleArray) {
          const {
            p,
            y,
            r
          } = this.faceAngle;
          const {
            global
          } = res.confArray;
          const g = this.faceCredibility;
          const {
            pitch,
            yaw,
            roll
          } = res.angleArray;
          const confArray = res.confArray;
          if (this.pitch_flag == false) {
            this.bottomTips = this.tips.pitch
          } else {
            if (this.roll_flag == false) {
              this.bottomTips = this.tips.roll
            }
          }
          if (this.yaw_old != 0 && (this.pitch_flag == false || this.yaw_flag == false)) {
            if (Math.abs(Math.abs(pitch) - Math.abs(this.pitch_old)) >= this.pitch_Threshold && this.pitch_flag ==
              false) {
              // console.log("点头");
              this.bottomTips = this.tips.roll
              this.pitch_flag = true
            }
            // console.log("点头:" + Math.abs(Math.abs(pitch) - Math.abs(this.pitch_old)))
            // console.log("摇头:" + Math.abs(Math.abs(yaw) - Math.abs(this.yaw_old)))
            if (Math.abs(Math.abs(yaw) - Math.abs(this.yaw_old)) >= this.roll_Threshold &&
              this.yaw_flag == false) {
              // console.log("摇头");
              this.bottomTips = '校验通过正在拍照，请正视摄像头正在拍照'
              this.yaw_flag = true
            }
          }
          this.pitch_old = pitch;
          this.roll_old = roll;
          this.yaw_old = yaw;
          for (let wwhm in confArray) {
            if (confArray[wwhm] < this.face_away) {
              return;
            }
          }
          const isGlobal = global >= g;
          const isPitch = Math.abs(pitch) <= p;
          const isYaw = Math.abs(yaw) <= y;
          const isRoll = Math.abs(roll) <= r;
          if (isGlobal && isPitch && isYaw && isRoll && this.pitch_flag == true && this.yaw_flag == true) {
            this.bottomTips = "拍照中..."
            if (this.pict_flag) {
              if(this.personImagesUploadFlag){
                console.log('防抖生效');
                return
              }
              console.log('this.pict_flag', this.pict_flag);
              this.bottomTips = '拍照成功'
              uni.showLoading({mask: true,title: '识别中'})
              this.ctx.takePhoto({
                quality: 'high',
                success: (res) => {
                  console.log('res.tempImagePath', res.tempImagePath);
                  // 调用上传人脸并验证
                  this.setImageContractSan(res.tempImagePath)
                }
              })
            } else {
              this.pict_flag = true
              setTimeout(() => {
                this.startphoto();
              }, 3000);
            }
          }
        } else {
          console.log('获取人脸识别数据失败', res);
          this.cancel()
        }
      },
      startphoto() {
        this.startRecord();
      },

      // 开始录制
      startRecord() {
        console.log('开始录制')
        this.ctx.takePhoto({
          quality: 'high',
          success: (res) => {
            this.setRecordingTips();
            console.log('拍照结果', res.tempImagePath);
            this.listener.stop();
            wx.stopFaceDetect();
            clearInterval(this.interval);
            this.isCompleteRecoding = false
          },
          fail: () => this.stop()
        })
      },
      // 设置录制中的提示文字和倒计时
      setRecordingTips() {
        let second = (3000 / 1000);
        if (this.interval) clearInterval(this.interval);
        this.interval = setInterval(() => {
          console.log('xxxxxx', second);
          this.bottomTips = this.tips.recording + second-- + 's'
          if (second <= 0) clearInterval(this.interval);
        }, 1000)
      },

      // 完成录制
      completeRecord() {
        console.log('完成录制');
        this.isCompleteRecoding = true
        this.ctx.stopRecord({
          compressed: this.compressed,
          success: (res) => {
            this.bottomTips = this.tips.complete
            // 向外触发完成录制的事件
          },
          fail: () => this.stop(),
          complete: () => {
            this.listener.stop();
            wx.stopFaceDetect();
            clearInterval(this.interval);
            this.isCompleteRecoding = false
          }
        })
      },
      // 人脸移出等取消录制
      cancel() {
        console.log('取消录制');
        // 如果不在录制中或者正在录制完成中就不能取消
        if (!this.isRecoding || this.isCompleteRecoding) return
        clearTimeout(this.timer);
        clearInterval(this.interval);
        this.ctx.stopRecord({
          complete: () => {
            console.log('取消录制成功');
            this.bottomTips = this.tips.ready
            this.isRecoding = false
          }
        });
      },
      // 用户切入后台等停止使用摄像头
      stop() {
        console.log('停止录制');
        clearTimeout(this.timer);
        clearInterval(this.interval);
        if (this.listener) this.listener.stop();
        if (this.ctx && !this.isCompleteRecoding) this.ctx.stopRecord()
        wx.stopFaceDetect();
        setTimeout(() => {
          this.bottomTips = ''
          this.isRecoding = false
        }, 500)
      },

      // 用户不允许使用摄像头
      error(e) {
        // const cameraName = 'scope.camera';
        // this.triggerEvent('noAuth', cameraName)
      },

      // 初始相机和录音权限
      async initAuthorize() {
        const cameraName = 'scope.camera';
        const recordName = 'scope.record';
        const scopeCamera = await getAuthorize(cameraName);
        // 未授权相机
        if (!scopeCamera) {
          // 用户拒绝授权相机
          console.log('// 用户拒绝授权相机');
          if (!(await setAuthorize(cameraName))) this.openSetting();
          return false;
        }
        const scopeRecord = await getAuthorize(recordName);
        console.log('用户授权录音结果', scopeRecord);
        if (!scopeRecord) {
          // 用户拒绝授权录音
          console.log('// 用户拒绝授权录音');
          if (!(await setAuthorize(recordName))) {
            this.openSetting();
            return false;
          }
        }
        console.log('初始相机和录音权限', true);
        return true;
      },

      navTo(id) {
        uni.redirectTo({
          url: './singename?id=' + id
        })
      },

      // 打开设置授权
      openSetting() {
        wx.showModal({
          title: '开启摄像头和录音权限',
          showCancel: true,
          content: '是否打开？',
          success: (res) => {
            // this.triggerEvent('noAuth', '打开设置授权')
            if (res.confirm) {
              wx.openSetting();
            }
          }
        });
      }
    },
    onLoad(options) {
      console.log('路径参数', options);
      this.id = options.id
    },
    onUnload() {
      uni.removeStorageSync('nowSubpackName')
      uni.removeStorageSync('nowrelativeUnitId')
      uni.removeStorageSync('nowrelativeProjectId')
    }
  };
</script>

<style>
  /* components/camera-face/index.wxss */
  .page-body {
    width: 100%;
    height: 100%;
    background: #ffffff;
  }

  .camera-box {
    width: 750rpx;
    height: 505rpx;
    margin-top: 100rpx;
    position: relative;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .camera {
    width: 480rpx;
    height: 480rpx;
    z-index: 1;
    border-radius: 50%;
  }

  .img-view {
    width: 750rpx;
    height: 505rpx;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 999;
  }

  .mask-img {
    width: 750rpx;
    height: 505rpx;
  }

  .bottom-view {
    width: 100%;
    height: 120rpx;
    background: rgba(1, 1, 1, .3);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 30rpx;
  }

  .bottom-btn {
    width: 300rpx;
    height: 70rpx;
    line-height: 70rpx;
    text-align: center;
    background: rgba(250, 250, 250, .8);
    border-radius: 35rpx;
  }

  .bottom-tips {
    width: 100%;
    height: 35rpx;
    color: #ffffff;
    line-height: 35rpx;
    text-align: center;
  }
</style>
